# Making packs: your server's own weapons, maps and characters

A server can add content of its own. Players download it from your server when they join, play
with it there, and it is gone from their game when they leave. This is how to make it.

Everything here is checked by `lsfpack` when you build, by the server when it starts and by every
player's game when it joins. A pack that breaks a rule is refused with the reason; nothing "mostly
works".

Contents
- [What a pack can and cannot do](#what-a-pack-can-and-cannot-do)
- [The folders](#the-folders)
- [pack.cfg](#packcfg)
- [A weapon](#a-weapon)
- [A map](#a-map)
- [A character](#a-character)
- [Building and checking](#building-and-checking)
- [Names](#names)
- [Limits](#limits)
- [What players see](#what-players-see)

## What a pack can and cannot do

- A pack **adds**. It never changes, replaces or re-skins anything of the base game: the AK-74 is
  the AK-74 on every server.
- A pack is **data only**: models, textures, sounds, maps and the numbers in `pack.cfg`. No
  script, shader or program has a place in one.
- What a pack adds exists **only on your server**. A player buys your weapon with SP in your
  server's shop and owns it there; it does not follow them to other servers.
- Your packs together may be **50 MB** at most.

## The folders

In your server's folder:

```
weapons/<pack>/pack.cfg     and the pack's files beside it
maps/<pack>/pack.cfg
chars/<pack>/pack.cfg
packs/                      what lsfpack builds: <pack>.lsfpack, served to players
tools/lsfpack               the builder
```

Each folder under `weapons/`, `maps/` or `chars/` becomes one pack. The files keep Soldier
Front's own layout:

| In | Lay your files out as | They go into |
|---|---|---|
| `weapons/<pack>/` | `bhw/sf_a_<gun>/...` | the weapon library |
| `maps/<pack>/` | `ground/sf_m_<map>/...` and `world/sf_m_<map>.wld` | the area library |
| `chars/<pack>/` | `sf_c_<name>/...` | the force library |

A top folder named `area`, `weapon`, `force`, `sound`, `effect`, `lobby` or `menu` puts what is
under it into that library instead (a map pack that ships a prop's texture in `area/...`, say).

File and folder names are lower case letters, digits, `_`, `-` and `.` only.

## pack.cfg

```
id = mypack              # lower case letters, digits and _, 3 to 24: unique on your server
version = 1
author = "Who made it"
```

then one section for each thing the pack adds: `[weapon]`, `[character]`, `[part]`, `[map]`.

## A weapon

```
[weapon]
name  = longrifle            # its code is x:mypack:longrifle; players' ownership is kept by it
title = "Long Rifle"         # what players read
like  = ak74                 # the base gun it starts from
price = 12000                # SP; a custom item is never free
damage = 38
magazine = 20
```

`like` names a base gun by its model id (`ak74`, `m4a1`, ...). Every number you do not give is
that gun's, and its **sounds, shop picture, HUD picture and kill mark** are used for yours.

Leave `model` out and the weapon uses the `like` gun's model: a new gun made of numbers alone.
To give it a model of its own:

```
model = bhw/sf_a_longrifle   # the folder beside pack.cfg
```

with the gun's files in it, named as Soldier Front names them:

```
bhw/sf_a_longrifle/sf_a_g_longrifle.lma        the gun
bhw/sf_a_longrifle/sf_a_b_longrifle.lma        its rig
bhw/sf_a_longrifle/sf_a_hand_longrifle.lma     the hands holding it (sf_a_hand.lma: the plain hands)
bhw/sf_a_longrifle/sf_a_m_longrifle_idle.lma   a clip an action: idle, shoot, reload, draw, move ...
bhw/sf_a_longrifle/sf_a_longrifle.sfc          where it sits in the view
bhw/sf_a_longrifle/sf_a_longrifle.sfm          its clips' speeds
```

Textures the model names are looked for in the gun's own folder first, then by name in the base
game. Using a base texture by its name is fine and costs none of your 50 MB.

The numbers you may set, and their bounds:

| Key | Bound |
|---|---|
| `class` | rifle, smg, sniper, machinegun, shotgun, pistol, knife, grenade. A pistol or a shotgun is a sidearm, a knife is the blade, a grenade is thrown: the slot follows the class |
| `damage` | 0 to 500 |
| `head`, `leg` | the multipliers for a head and a leg hit |
| `range`, `falloff_start`, `falloff_min` | centimetres, and what is left of the damage at range |
| `rpm` | 1 to 1,200 |
| `automatic` | yes or no |
| `magazine`, `reserve` | 0 to 300, 0 to 999 |
| `reload`, `draw` | seconds |
| `spread_stand`, `spread_crouch`, `spread_move`, `spread_move_rate`, `spread_per_shot`, `spread_max`, `spread_recover`, `spread_run`, `spread_air`, `spread_walk` | the cone, in degrees and multipliers |
| `recoil_up`, `recoil_up_max`, `recoil_side`, `recoil_side_max`, `recoil_recover` | the view's kick |
| `move_speed` | how fast its owner runs with it; too high and the server would refuse the movement, so it is refused here |
| `grip` | 1 to 7, 9 or 10: which arm clips the soldier holds it with |
| `carried` | the model seen in a soldier's hands (a base one, by name); left out: the `like` gun's |
| `zoom`, `scope_image`, `scope_move` | a scope |
| `pellets` | 1 to 16 |
| `grenade`, `fuse`, `blast_radius` | none, frag, flash, smoke or gas; seconds; 0 to 2,000 cm |
| `melee_range` | a blade's reach |
| `price` | 1 to 10,000,000 SP |

## A map

```
[map]
name  = harbour              # its id is x-mypack-harbour (at most 32 characters in all)
title = "Harbour"
night = false                # true for a map baked dark
```

with the map as SFMapEditor (or the original tools) makes it:

```
ground/sf_m_harbour/sf_m_harbour.map           geometry
ground/sf_m_harbour/sf_m_harbour.msf           its textures' table
ground/sf_m_harbour/sf_m_harbour.xml           the world script: spawns, objectives, the games it offers
ground/sf_m_harbour/sf_m_harbour_c.cft         collision
ground/sf_m_harbour/sf_m_harbour_obj.env       props
ground/sf_m_harbour/level1/sector_NNlightingmap.dds   lightmaps
world/sf_m_harbour.wld
```

Which games the map offers is read from its world script, exactly as for the base maps.

A map's textures are most of its size. Textures and props the map names are found by name in the
base game, so reuse them where you can and ship only what is new.

When the pack is built the map is proved as the base maps are: it loads whole, every texture
resolves, both sides have somewhere to spawn, there is collision to stand on and ground to walk,
and its world script offers at least one game. The build prints what it found.

## A character

```
[character]
name   = ranger
title  = "Ranger"
nation = "USA"
model  = sf_c_ranger         # the folder beside pack.cfg
art    = delta               # the base force whose HUD pictures and voice stand in
price  = 30000
speed  = 1.0                 # 0.8 to 1.04
upper_defense = 0            # 0 to 0.05, like lower_defense and avoid_headshot
```

with the rig and the pieces:

```
sf_c_ranger/sf_c_ranger_bone.lma       the skeleton
sf_c_ranger/sf_c_ranger_head.lma       its pieces, a body part each (.lma or .fxa)
sf_c_ranger/sf_c_ranger_upperbody.lma
...                                    and their textures
```

- A character is **only a look**. Every soldier is hit by the same boxes whatever the model, so
  one much bigger or smaller than the base soldiers is refused: nobody is harder to see than to hit.
- Its skeleton has at most **128 bones** and uses the base soldiers' bone names: the game plays
  the base soldiers' own motions on it, and a skeleton that cannot play every one of them is refused.
- The bones, the size and the clips are **measured from your model** when the pack is built. You
  do not type them.

A part (a piece of clothing, an accessory):

```
[part]
name  = ranger_cap
title = "Ranger Cap"
tab   = Head                 # Head, Face, Torso, Arms, Legs, Feet or Accessory
slot  = 3
character = ranger           # one of this pack's characters
model = sf_o_ranger_cap      # the piece's file name: no folder, no extension
price = 2000
```

A pack character wears only its own pack's parts. A pack may also add a new part for a **base**
force (`force = delta` instead of `character = ...`): a new item, never a re-skin of an old one.

## Building and checking

```
tools/lsfpack build --data data
```

builds every pack folder into `packs/`. `--data` is the Soldier Front game data (your server's
`data/`). Each pack is checked on the way, and all of them together.

```
tools/lsfpack check packs --data data      what the server will do when it starts
tools/lsfpack list packs/mypack.lsfpack    what a pack holds
```

Restart the server to serve new packs. A server's packs never change while it runs, so no player
ever sees half of an update. A server that finds a broken pack in `packs/` does not start, and
says which pack and why.

## Names

- Every file of a pack lives under `x/<pack id>/` inside the game, so it can never take the place
  of a base file.
- A file may not be called what **one** base file is called (the base game finds many files by
  name alone, and a second of the name would hide the first). The build says which name. A
  picture counts under any extension: `wall.png` clashes with a base `wall.jpg`.
- A new name belongs to one pack on your server: two of your packs cannot both ship `crate.dds`.
- A map's lightmaps (`level1/sector_NNlightingmap`) are named by the map format and are free.
- Nothing of the base game ever finds a pack's file by name, and no pack finds another pack's.

## Limits

| What | Limit |
|---|---|
| A server's packs together | 50 MB |
| Files in a pack | 4,096 |
| A file's name with its folders | 120 characters |
| A picture | 4,096 a side (2,048 for players on Android to be able to join) |
| A sound | 8 MB |
| A skeleton | 128 bones |
| Kinds of file | area: `.map .msf .xml .cft .env .wld .val .osf .db .particle`; weapon: `.lma .sfc .sfm .fpd`; force: `.lma .fxa .fxm .lmf .sfc .sfm .csv`; sound: `.wav .mp3` (the two the game plays); pictures anywhere but the sound library: `.dds .tga .jpg .png .bmp` |

## What players see

Joining your server, a player's game is told what your packs are, shows their size and asks
before downloading more than the player's own threshold, downloads them from your server (at most
`upload_kbps` of your upload in all, `max_downloaders` players at a time, the rest waiting their
turn), checks every one again itself, and only then enters. Packs are kept in the player's cache
by their fingerprint, so the next join downloads nothing.

A match played with your packs is recorded with their fingerprints: the recording plays back only
where those packs are.
